// SPDX-FileCopyrightText: 2023-2025 Sangfor Technologies Inc.
// SPDX-License-Identifier: Mulan PSL v2
package com.geniusai.aip.auth.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.geniusai.aip.auth.config.ExternalConfig;
import com.geniusai.aip.auth.config.GpuResourceConfig;
import com.geniusai.aip.auth.constants.*;
import com.geniusai.aip.auth.domain.*;
import com.geniusai.aip.auth.domain.convertor.BaseConverter;
import com.geniusai.aip.auth.domain.convertor.IdConverter;
import com.geniusai.aip.auth.domain.convertor.UserVoConverter;
import com.geniusai.aip.auth.domain.edsentity.ObjectUsage;
import com.geniusai.aip.auth.exception.AuthLoginException;
import com.geniusai.aip.auth.exception.AuthLoginExceptionEnum;
import com.geniusai.aip.auth.mapper.NotebooksMapper;
import com.geniusai.aip.auth.mapper.UserMapper;
import com.geniusai.aip.auth.service.*;
import com.geniusai.aip.auth.utils.BcryptUtil;
import com.geniusai.aip.auth.utils.DecimalUtils;
import com.geniusai.aip.auth.utils.HttpUtil;
import com.geniusai.aip.feature.common.utils.StringUtils;
import com.geniusai.aip.feature.common.utils.executor.TaskExecutorContext;
import com.geniusai.aip.feature.common.utils.json.JacksonUtils;
import com.geniusai.aip.feature.common.web.Response;
import com.geniusai.aip.feature.common.web.common.PageModel;
import com.geniusai.aip.feature.common.web.utils.UrlUtils;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.ArrayUtils;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIUtils;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.message.BasicNameValuePair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static com.geniusai.aip.auth.constants.AuthCommonConstants.*;
import static com.geniusai.aip.auth.constants.LoadConstants.*;

/**
 * @author hml14563
 * @date 2021/3/19
 */
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, UserDO> implements UserService {
    @Autowired
    private UserMapper userdomapper;

    @Autowired
    private NotebooksMapper notebooksMapper;

    @Autowired
    private ExternalConfig externalConfig;

    @Autowired
    private ProfileService profileService;

    @Autowired
    private EnvoyFilterService envoyFilterService;

    @Autowired
    private MetricService metricService;

    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private RestDataSpaceService dataSpaceService;

    @Autowired
    private RestDataAuthService dataAuthService;

    /**
     * 判断是不是重名
     *
     * @param name 对应的名称
     */
    private void isDuplicateName(String name) {
        QueryWrapper<UserDO> query = new QueryWrapper<>();
        query.eq("username", name);
        if (userdomapper.selectCount(query) > 0) {
            throw new AuthLoginException(AuthLoginExceptionEnum.DUPLICATED_NAME_ERROR);
        }
    }

    @Override
    public UserDO createEdsUserBucket(UserDO userDO) {
        UserDO edsUserResponse = createEdsUser(userDO);
        userDO.setS3AccessKey(edsUserResponse.getS3AccessKey());
        userDO.setS3SecretKey(edsUserResponse.getS3SecretKey());
        userDO.setS3EncryptSecretKey(edsUserResponse.getS3EncryptSecretKey());
        return userDO;
    }

    @Override
    public UserVO createUser(UserDO userDO) {
        isDuplicateName(userDO.getUsername());
        String encryptPwd = BcryptUtil.bEncryptEncode(new String(ArrayUtils.toPrimitive(userDO.getPassword())));
        userDO.setPassword(ArrayUtils.toObject(encryptPwd.getBytes()));
        // 用户名作为唯一表示符
        userDO.setUserId(IdConverter.username2id(userDO.getUsername()));

        // 创建MinIO,profile,EnvoyFilter
        createThirdPartyUser(userDO);

        userdomapper.insert(userDO);
        log.info("成功将用户信息更新到数据库user表,用户名:{}", userDO.getUsername());
        return new BaseConverter<UserDO, UserVO>().convert(userDO, UserVO.class);
    }

    private void createThirdPartyUser(UserDO userDO) {
        Future<Boolean> futureTask = null;
        try {
            if (MINIO_STORAGE_TYPE.equals(externalConfig.getObjectStorageType())) {
                String username = userDO.getUsername();
                String secretKey = UUID.randomUUID().toString();
                userDO.setS3SecretKey(secretKey);
                userDO.setS3AccessKey(username);
                userDO.setS3EncryptSecretKey("");
                // 执行创建Minio用户的逻辑
                log.info("开始创建Minio的用户:{}", username);
                futureTask = TaskExecutorContext.getTaskExecutor().submit(() -> createMinioUser(username, secretKey));
                log.info("开始创建Minio的用户:{}", username);
            } else if (EDS_STORAGE_TYPE.equals(externalConfig.getObjectStorageType())) {
                userDO = createEdsUserBucket(userDO);
            } else {
                throw new AuthLoginException(AuthLoginExceptionEnum.UNSUPPORT_OBJECT_STORAGE_TYPE);
            }
        } catch (Exception e) {
            log.error("创建minio失败：{}" + e);
            throw new AuthLoginException(AuthLoginExceptionEnum.FAIL_CREATE_MINIO_USER);
        }

        // 创建profile对象
        UserDO finalUserDO = userDO;
        Profile profile = new Profile() {
            {
                setUsername(finalUserDO.getUsername());
                setStorage(String.valueOf(finalUserDO.getVolumeSize()));
            }
        };

        try {
            log.info("开始创建profile的用户:{}", JacksonUtils.toJson(profile));
            profileService.createProfile(profile);
            log.info("成功创建profile的用户:{}", JacksonUtils.toJson(profile));
        } catch (Exception e) {
            log.error("{} 创建profile失败", userDO.getUsername());
            log.error("创建profile失败{}", e);
            throw new AuthLoginException(AuthLoginExceptionEnum.CREATE_PROFILE_ERROR);
        }

        // TODO create comfigmap for mounting ~/.mc/config.json
        log.info("即将创建profile的用户:{}, s3SecreteKey:{}", userDO.getUsername(), userDO.getS3SecretKey());
        profileService.createSecret(
                SECRET_NAME, userDO.getUsername(),
                Base64.getEncoder().encodeToString(userDO.getS3AccessKey().getBytes()),
                Base64.getEncoder().encodeToString(userDO.getS3SecretKey().getBytes())
        );

        profileService.createServiceAccount(SA_NAME, userDO.getUsername(), SECRET_NAME);
        log.info("开始创建harbor用户:{}", userDO.getUsername());
        int harborProjectId = createHarborProject(userDO.getUsername());
        log.info("成功创建harbor用户:{}, harborProjectId:{}", userDO.getUsername(), harborProjectId);

        log.info("开始创建mysql-secret的用户:{}", userDO.getUsername());
        profileService.createMysqlSecret(MYSQL_SECRET_NAME, userDO.getUsername());
        log.info("成功创建mysql-secret的用户:{}", userDO.getUsername());

        log.info("开始创建角色信息的用户:{}", userDO.getUsername());
        profileService.createUserRole(USER_ROLE, userDO.getUsername());
        log.info("成功创建角色信息的用户:{}", userDO.getUsername());

        log.info("开始创建orion用户:{}", userDO.getUsername());
        createOrUpdateOrionQuota(userDO.getUsername(), userDO.getVgpuMemory());
        log.info("成功创建orion用户:{}", userDO.getUsername());

        // 创建EnvoyFilter对象
        EnvoyFilter envoyFilter = new EnvoyFilter() {
            {
                setNamespace(finalUserDO.getUsername());
            }
        };
        try {
            envoyFilterService.createEnvoyFilter(finalUserDO.getUsername(), envoyFilter);
        } catch (Exception e) {
            log.error("{} 创建envoyFilter失败" + e.getMessage());
            throw new AuthLoginException(AuthLoginExceptionEnum.CREATE_ENVOYFILTER_ERROR);
        }

        userDO.setProjectId(harborProjectId);

        try {
            futureTask.get();
        } catch (InterruptedException e) {
            log.error("创建minio失败,中断异常:{}" + e);
            throw new AuthLoginException(AuthLoginExceptionEnum.FAIL_CREATE_MINIO_USER);
        } catch (ExecutionException e) {
            log.error("创建minio失败:{}" + e);
            throw new AuthLoginException(AuthLoginExceptionEnum.FAIL_CREATE_MINIO_USER);
        }
    }

    private void createOrUpdateOrionQuota(String username, Integer vGpuMemory) {
        String createOrionClientIdUrl = "http://" + externalConfig.getOrionControllerService() + ":" + externalConfig.getOrionControllerPort() + "/" + CREATE_OR_DELETE_ORION_CLIENT_ID_URL + "/"
                + username;
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.add("Content-Type", "application/json");

        OrionQuata orionQuata = new OrionQuata();
        orionQuata.setVdeviceMem(vGpuMemory * 1024);
        orionQuata.setSingleVdeviceMem(vGpuMemory * 1024);
        orionQuata.setSingleVdeviceRatio(100);

        String content = JacksonUtils.toJson(orionQuata);
        try {
            HttpEntity<String> httpEntity = new HttpEntity<>(content, httpHeaders);
            HttpEntity<String> response = restTemplate.postForEntity(createOrionClientIdUrl, httpEntity, String.class);
            String result = response.getBody();
            log.info("创建Orion-ClientId结果: {}" + result);
        } catch (Exception e) {
            log.info("创建Orion-ClientId结果: {}" + e);
        }

    }

    private void deleteOrionQuota(String username) {
        String deleteOrionClientIdUrl = "http://" + externalConfig.getOrionControllerService() + ":" + externalConfig.getOrionControllerPort() + "/" + CREATE_OR_DELETE_ORION_CLIENT_ID_URL + "/"
                + username;
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.add("Content-Type", "application/json");

        try {
            restTemplate.delete(deleteOrionClientIdUrl);
        } catch (Exception e) {
            log.info("创建Orion-ClientId失败: {}" + e);
        }

    }

    private Boolean createMinioUser(String userId, String password) throws AuthLoginException {
        log.info("creating minio user {}......", userId);
        try {
            String adminAccessKey = new String(Base64.getDecoder().decode(externalConfig.getMinioAccessKey()), StandardCharsets.UTF_8);
            String adminSecretKey = new String(Base64.getDecoder().decode(externalConfig.getMinioSecretKey()), StandardCharsets.UTF_8);
            String commandStr = String.format(
                    "/opt/aip/add_minio_user_bucket.sh %s %s %s:%s %s %s",
                    userId,
                    password,
                    "http://" + externalConfig.getMinioService(),
                    externalConfig.getMinioServicePort(),
                    adminAccessKey,
                    adminSecretKey
            );
            log.info("create minio user command: {}", commandStr);
            // 开启一个process执行命令
            execCommand(commandStr);
        } catch (Exception e) {
            log.error("创建minio失败" + e.getMessage());
            throw new AuthLoginException(AuthLoginExceptionEnum.FAIL_CREATE_MINIO_USER);
        }
        return true;
    }

    public void execCommand(String commandStr) throws Exception {
        Process process = Runtime.getRuntime().exec(commandStr);
        int exitValue = process.waitFor();
        if (0 != exitValue) {
            BufferedReader error = new BufferedReader(new InputStreamReader(process.getErrorStream()));
            log.error("create minio user error, code: {}, message: {}", exitValue, error.readLine());
            error.close();
            log.error("创建minio失败");
            throw new AuthLoginException(AuthLoginExceptionEnum.FAIL_CREATE_MINIO_USER);
        }
    }

    private int createHarborProject(String username) {
        String harborUri = "http://" + externalConfig.getHarborService() + HARBOR_PROJECT_URL;
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.add("authorization", AUTHORIZATION);
        httpHeaders.add("Content-Type", "application/json");

        HarborProject harborProject = new HarborProject();
        harborProject.setProjectName(username);

        String content = JacksonUtils.toJson(harborProject);
        HttpEntity<String> httpEntity = new HttpEntity<>(content, httpHeaders);
        HttpEntity<String> response = restTemplate.postForEntity(harborUri, httpEntity, String.class);
        String location = Objects.requireNonNull(response.getHeaders().get("Location")).get(0);
        return Integer.parseInt(location.substring(HARBOR_PROJECT_URL.length() + 1));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<UserVO> createUsers(UsersVO usersVO) {
        List<UserVO> userVOList = new ArrayList<>();
        List<String> usernames = usersVO.getUsernames();
        if (usernames.size() < 1) {
            throw new AuthLoginException(AuthLoginExceptionEnum.EMPTY_USERNAME);
        }

        // 以下代码逻辑允许资源超分配
//        int size = usernames.size();
//        float cpuNum = usersVO.getCpuNum();
//        int memorySize = usersVO.getMemorySize();

//        int leftCpu = EXTRA_AMOUNT_RATIO * metricService.getTotalCpu().intValue() - metricService.getRequestTotalCpu().intValue();
//        int leftMemory = EXTRA_AMOUNT_RATIO * metricService.getTotalMemory().intValue() - metricService.getRequestTotalMemory().intValue();

//        if (size * cpuNum > leftCpu || size * memorySize > leftMemory) {
//            log.error("用户CPU资源超配，请重新配置");
//            throw new AuthLoginException(AuthLoginExceptionEnum.RESOURCE_EXCEEDED_ERROR);
//        }

        for (String username : usernames) {
            if (com.geniusai.aip.auth.utils.StringUtils.isValidPassword(username)) {
                throw new AuthLoginException(AuthLoginExceptionEnum.USERNAME_FORMAT_ERROR);
            }
            UserDO userDO = new UserDO();
            userDO.setEmail(StringUtils.getUserEmail(username));
            userDO.setRole(usersVO.getRole());
            userDO.setVgpuMemory(usersVO.getVGpuMemory());
            userDO.setMemorySize(usersVO.getMemorySize());
            userDO.setVolumeSize(usersVO.getVolumeSize());
            userDO.setCpuNum(usersVO.getCpuNum());
            userDO.setPassword(ArrayUtils.toObject(usersVO.getPassword().getBytes()));
            userDO.setUsername(username);
            userDO.setIsFirstLogin(1);
            UserVO userVO = createUser(userDO);

            userVOList.add(userVO);
        }
        //如果是管理员，分配权限管理员角色
        dataAuthService.grantDataAuthRoleIfAdminAsync(usersVO.getRole(), usernames.get(0));
        //创建数据空间
        dataSpaceService.createDataSpaceAsync(usersVO);
        return userVOList;
    }

    private void deleteMinioUser(String userId) throws AuthLoginException {
        log.info("deleting minio user {}......", userId);
        try {
            String adminAccessKey = new String(Base64.getDecoder().decode(externalConfig.getMinioAccessKey()), StandardCharsets.UTF_8);
            String adminSecretKey = new String(Base64.getDecoder().decode(externalConfig.getMinioSecretKey()), StandardCharsets.UTF_8);
            String commandStr = String.format(
                    "/opt/aip/delete_minio_user_bucket.sh %s %s:%s %s %s",
                    userId,
                    "http://" + externalConfig.getMinioService(),
                    externalConfig.getMinioServicePort(),
                    adminAccessKey,
                    adminSecretKey
            );
            log.info("delete minio user command: {}", commandStr);
            Process process = Runtime.getRuntime().exec(commandStr);
            int exitValue = process.waitFor();
            if (0 != exitValue) {
                BufferedReader error = new BufferedReader(new InputStreamReader(process.getErrorStream()));
                log.error("delete minio user error, code: {}, message: {}", exitValue, error.readLine());
                error.close();
                throw new AuthLoginException(AuthLoginExceptionEnum.FAIL_DELETE_MINIO_USER);
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new AuthLoginException(AuthLoginExceptionEnum.FAIL_DELETE_MINIO_USER);
        }
    }

    private UserDO createEdsUser(UserDO userDO) {
        UserDO edsUserResponse = externalConfig.resourceType().edsCreateEntrance(userDO);
        log.info(
                "creating EDS user userId:{}, userName:{}, accessKey:{}, secretKey:{}", edsUserResponse.getUserId(), edsUserResponse.getUsername(), edsUserResponse.getS3AccessKey(),
                edsUserResponse.getS3SecretKey()
        );
        return edsUserResponse;
    }

    @Override
    public void deleteEdsUser(String userId) {
        log.info("deleting EDS user {}......", userId);
        UserDO userDO = userdomapper.selectOne(new QueryWrapper<UserDO>().eq("user_id", userId));
        List<String> deleteList = new ArrayList<>();
        deleteList.add(userDO.getUsername());
        externalConfig.resourceType().edsDeleteEntrance(deleteList, deleteList);
    }


    @Override
    public void changePassword(ChangePasswordEntity changePasswordEntity, String userId) {

        String oldPassword = changePasswordEntity.getOldPwd();
        String newPassword = changePasswordEntity.getNewPwd();
        if (!checkOldPassword(oldPassword, userId)) {
            throw new AuthLoginException(AuthLoginExceptionEnum.PASSWORD_ERROR);
        }

        String dbPwd = BcryptUtil.bEncryptEncode(newPassword);
        UserDO user = userdomapper.selectOne(new QueryWrapper<UserDO>().eq("username", userId));

        AtomicBoolean isMatchOldPwd = new AtomicBoolean(false);
        Optional.ofNullable(user).ifPresent(
                val -> isMatchOldPwd.set(BcryptUtil.bEncryptMatch(newPassword, Optional.of(new String(ArrayUtils.toPrimitive(val.getPassword()))).orElse("")))
        );
        if (isMatchOldPwd.get()) {
            log.info("The same pwd as before");
            throw new AuthLoginException(AuthLoginExceptionEnum.SAME_PASSWORD_ERROR);
        }
        assert user != null;
        user.setPassword(ArrayUtils.toObject(dbPwd.getBytes()));
        userdomapper.update(user, new UpdateWrapper<UserDO>().eq("user_id", userId));
    }

    @Override
    public Boolean queryUserByUsernameAndPassword(String username, String password) {
        return checkOldPassword(password, username);
    }

    @Override
    public Boolean isFirstLogin() {
        // 判断是不是第一次登陆
        String rawAdminPwd = externalConfig.getAdminPwd();
        Byte[] dbAdminPwd = userdomapper.selectOne(new QueryWrapper<UserDO>().eq("username", externalConfig.getAdminName())).getPassword();
        return BcryptUtil
                .bEncryptMatch(rawAdminPwd, new String(ArrayUtils.toPrimitive(dbAdminPwd)));
    }

    @Override
    public Integer getRoleByUsername(String username) {
        UserDO user = userdomapper.selectOne(new QueryWrapper<UserDO>().eq("username", username));
        if (user == null) {
            throw new AuthLoginException(AuthLoginExceptionEnum.USERNAME_NOT_FOUND);
        }
        return user.getRole();
    }

    @Override
    public String getS3AccessKeyByUsername(String username) {
        UserDO user = userdomapper.selectOne(new QueryWrapper<UserDO>().eq("username", username));
        if (user == null) {
            throw new AuthLoginException(AuthLoginExceptionEnum.USERNAME_NOT_FOUND);
        }
        return user.getS3AccessKey();
    }

    @Override
    public String getS3SecretKeyByUsername(String username) {
        UserDO user = userdomapper.selectOne(new QueryWrapper<UserDO>().eq("username", username));
        if (user == null) {
            throw new AuthLoginException(AuthLoginExceptionEnum.USERNAME_NOT_FOUND);
        }
        return user.getS3SecretKey();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteUser(String userId) {

        try {
            profileService.deleteProfile(userId);
            try {
                // 删除对应的envoyFilter
                envoyFilterService.deleteEnvoyFilter(userId);
            } catch (IOException e) {
                log.error("{} envoyfilter 删除失败", userId);
                throw new AuthLoginException(AuthLoginExceptionEnum.DELETE_ENVOYFILTER_ERROR);
            }

            deleteHarborProject(userId);
            // delete notebook mysql record
            try {
                restTemplate.delete(
                        UrlUtils.createUrl(
                                externalConfig.getCodeStudioService(),
                                externalConfig.getCodeStudioServicePort(), DELETE_USERID_NOTEBOOK_RECORDS_URL
                        ), userId
                );
            } catch (HttpServerErrorException ex) {
                log.error("{} notebooks records 删除失败", userId);
                throw new AuthLoginException(AuthLoginExceptionEnum.DELETE_USERID_NOTEBOOKS_RECORDS_ERROR, ex);
            }

            // delete images record in mysql
            try {
                restTemplate.delete(
                        UrlUtils.createUrl(
                                externalConfig.getServiceManage(),
                                externalConfig.getServiceManagePort(), DELETE_USERID_IMAGES_RECORDS_URL
                        ), userId
                );
            } catch (HttpServerErrorException ex) {
                log.error("{} images records 删除失败", userId);
                throw new AuthLoginException(AuthLoginExceptionEnum.DELETE_USERID_IMAGES_RECORDS_ERROR, ex);
            }

            // delete the project of the user
            try {
                restTemplate.delete(
                        UrlUtils.createUrl(
                                externalConfig.getWebService(),
                                externalConfig.getWebServicePort(), DELETE_PROJECT_URL
                        ), userId
                );
            } catch (HttpServerErrorException ex) {
                log.error("{} user project 删除失败", userId);
                throw new AuthLoginException(AuthLoginExceptionEnum.DELETE_PROJECT_ERROR, ex);
            }

            // delete the dataSource record in mysql
            try {
                restTemplate.delete(
                        UrlUtils.createUrl(
                                externalConfig.getWebService(),
                                externalConfig.getWebServicePort(), DELETE_DATASOURCE_URL
                        ), userId
                );
            } catch (HttpServerErrorException ex) {
                log.error("{} dataSource 删除失败", userId);
                throw new AuthLoginException(AuthLoginExceptionEnum.DELETE_DATASOURCE_ERROR, ex);
            }

            String objectStorageType = externalConfig.getObjectStorageType();
            if (MINIO_STORAGE_TYPE.equals(objectStorageType)) {
                deleteMinioUser(userId);
            } else if (EDS_STORAGE_TYPE.equals(objectStorageType)) {
                deleteEdsUser(userId);
            } else {
                throw new AuthLoginException(AuthLoginExceptionEnum.UNSUPPORT_OBJECT_STORAGE_TYPE);
            }

            // 删除orion quota
            deleteOrionQuota(userId);
        } catch (IOException e) {
            log.error("{} 删除失败", userId);
            throw new AuthLoginException(AuthLoginExceptionEnum.DELETE_PROFILE_ERROR);
        }

        userdomapper.delete(new QueryWrapper<UserDO>().eq("user_id", userId));
        //删除数据权限
        dataAuthService.revokeAllDataAuthAsync(userId);
        //删除数据空间
        dataSpaceService.deleteDataSpaceAsync(userId);
    }

    private void deleteHarborProject(String userId) {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.add("authorization", AUTHORIZATION);
        httpHeaders.add("Content-Type", "application/json");

        HttpEntity<String> entity = new HttpEntity<>(httpHeaders);
        UserDO user = userdomapper.selectOne(new QueryWrapper<UserDO>().eq("user_id", userId));
        // get all the repositories of the project
        int projectId = user.getProjectId();
        String getReposUrl = "http://" + externalConfig.getHarborService() + HARBOR_REPOSITORY_URL + "?project_id=" + projectId;
        HarborRepository[] response = restTemplate.getForObject(getReposUrl, HarborRepository[].class);
        assert response != null;
        // delete all the repos
        for (HarborRepository repository : response) {
            String name = repository.getName();
            String deleteRepoUrl = "http://" + externalConfig.getHarborService() + HARBOR_REPOSITORY_URL + "/" + name;
            restTemplate.exchange(deleteRepoUrl, HttpMethod.DELETE, entity, String.class);
        }
        // delete the project
        String deleteProjectUrl = "http://" + externalConfig.getHarborService() + HARBOR_PROJECT_URL + "/" + projectId;
        restTemplate.exchange(deleteProjectUrl, HttpMethod.DELETE, entity, String.class);
    }

    @Override
    public PageModel<UserVO> getUser(String keyword) {
        PageModel<UserVO> user;
        if (keyword.isEmpty() || "".equals(keyword.trim())) {
            user = findUser();
        } else {
            user = findUserWithKeyword(keyword);
        }
        //关联数据空间数据
        dataSpaceService.setDataSpaceInfo(user);
        return user;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public UserVO editUser(String userId, EditEntity editEntity) {
        UserDO userDO = userdomapper.selectOne(new QueryWrapper<UserDO>().eq("user_id", userId));
        if (userDO == null) {
            throw new AuthLoginException(AuthLoginExceptionEnum.USERID_INVALID_ERROR);
        }
        if (!editEntity.getPassword().isEmpty()) {
            if (Pattern.matches(PASSWORD_FORMAT, editEntity.getPassword())) {
                userDO.setPassword(ArrayUtils.toObject(BcryptUtil.bEncryptEncode(editEntity.getPassword()).getBytes()));
            } else {
                throw new AuthLoginException(AuthLoginExceptionEnum.PASSWORD_FORMAT_ERROR);
            }
        }
        userDO.setRole(editEntity.getRole());
        userDO.setCpuNum(editEntity.getCpuNum());
        userDO.setVgpuMemory(editEntity.getVGpuMemory());
        userDO.setMemorySize(editEntity.getMemorySize());
        userDO.setVolumeSize(editEntity.getVolumeSize());

        userdomapper.update(userDO, new UpdateWrapper<UserDO>().eq("user_id", userId));

        Profile profile = new Profile() {
            {
                setUsername(userId);
                setStorage(String.valueOf(editEntity.getVolumeSize()));
            }
        };

        try {
            profileService.updateProfile(profile);
        } catch (Exception e) {
            throw new AuthLoginException(AuthLoginExceptionEnum.EDIT_PROFILE_ERROR);
        }
        createOrUpdateOrionQuota(userDO.getUsername(), userDO.getVgpuMemory());
        //编辑数据权限角色
        dataAuthService.editDataAuthRoleAsync(userDO.getRole(),userDO.getUsername());
        //编辑数据空间： 四种情况：0不操作，1新增，2修改，3删除
        editEntity.setUsername(userId);
        dataSpaceService.editDataSpaceAsync(editEntity);
        return new UserVoConverter(externalConfig, metricService, restTemplate).convert(userDO, UserVO.class);
    }



    @Override
    public TotalResource getResource() {
        TotalResource resource = new TotalResource();
        Map<String, Double> totalResourceMap = metricService.getTotalResource();
        log.info("全局资源情况：{}" + totalResourceMap);
        Double totalCpu = totalResourceMap.getOrDefault(ResourceConstants.CPU, (double) 0);
        Double totalUsedCpu = totalResourceMap.getOrDefault(ResourceConstants.USED_CPU, (double) 0);
        Double totalMemory = totalResourceMap.getOrDefault(ResourceConstants.MEMORY, (double) 0);
        Double totalUsedMemory = totalResourceMap.getOrDefault(ResourceConstants.USED_MEMORY, (double) 0);

        Map<String, Double> requestTotalResourceMap = metricService.getRequestTotalResource();
        Double totalRequestCpu = requestTotalResourceMap.getOrDefault(ResourceConstants.TOTAL_REQUEST_CPU, (double) 0);
        Double totalRequestMemory = requestTotalResourceMap.getOrDefault(ResourceConstants.TOTAL_REQUEST_MEMORY, (double) 0);

        resource.setTotalCpu(totalCpu.intValue());
        resource.setTotalUsedCpu(totalUsedCpu.intValue());
        resource.setTotalRemainedCpu(totalCpu.intValue() - totalUsedCpu.intValue());
        resource.setTotalCpuUsage(getUsagePercent(totalUsedCpu, totalCpu));
        resource.setAllocateTotalCpu(totalRequestCpu.intValue());
        resource.setRemainedAllocatedTotalCpu(getRemainAllocatedResource(EXTRA_AMOUNT_RATIO * totalCpu.intValue(), totalRequestCpu.intValue()));
        resource.setAllocatedTotalCpuUsage(getUsagePercent(totalRequestCpu, totalCpu));

        resource.setTotalMemory(totalMemory.intValue());
        resource.setTotalUsedMemory(totalUsedMemory.intValue());
        resource.setTotalRemainedMemory(totalMemory.intValue() - totalUsedMemory.intValue());
        resource.setTotalMemoryUsage(getUsagePercent(totalUsedMemory, totalMemory));
        resource.setAllocatedTotalMemory(totalRequestMemory.intValue());
        resource.setRemainedAllocatedTotalMemory(getRemainAllocatedResource(EXTRA_AMOUNT_RATIO * totalMemory.intValue(), totalRequestMemory.intValue()));
        resource.setAllocatedTotalMemoryUsage(getUsagePercent(totalRequestMemory, totalMemory));

        // TODO
//        setResourceStorage(resource);
        resource.setAllocatedTotalStorage(-1);
        resource.setRemainedAllocatedTotalStorage(-1);
        resource.setTotalStorage(-1);

        // vGpu规格利用率柱状图
        resource.setTotalGpuMemory(GpuResourceConfig.getTotalGpuMemory());
        resource.setAllocatedTotalVGpuMemory(metricService.getAllocatedTotalVGpuMemory());
        resource.setAllocatedTotalVGpuMemoryUsage(getUsageOver100Percent(resource.getAllocatedTotalVGpuMemory(), resource.getTotalGpuMemory()));
        resource.setVGpuAllocatedGraphList(metricService.getTotalVGpuMemoryAllocateUsage());
        return resource;
    }

    private void setResourceStorage(TotalResource resource) {
        ObjectUsage objectUsage = externalConfig.resourceType().getObjectStorage();
        log.info("externalConfig.resourceType(): " + externalConfig.resourceType());
        log.info("objectUsage: " + JacksonUtils.toJson(objectUsage));
        resource.setTotalStorage(DecimalUtils.bytes2Gb(objectUsage.getTotalBytes()));
        resource.setTotalUsedStorage(DecimalUtils.bytes2Gb(objectUsage.getTotalUsedBytes()));
        resource.setTotalRemainedStorage(DecimalUtils.bytes2Gb(objectUsage.getTotalAvailBytes()));
        resource.setTotalStorageUsage(getUsagePercent(resource.getTotalUsedStorage(), resource.getTotalStorage()));
        resource.setAllocatedTotalStorage(resource.getTotalUsedStorage());
        resource.setRemainedAllocatedTotalStorage(resource.getTotalRemainedStorage());
        resource.setAllocatedTotalStorageUsage(resource.getTotalStorageUsage());
    }

    private int getUsagePercent(double usedResource, double totalResource) {
        if (usedResource > totalResource) {
            return 100;
        } else {
            return totalResource == 0 ? 0 : (int) (usedResource * 100 / totalResource);
        }
    }

    private int getUsageOver100Percent(double usedResource, double totalResource) {
        return totalResource == 0 ? 0 : (int) (usedResource * 100 / totalResource);
    }

    private int getRemainAllocatedResource(int totalResource, int requestResource) {
        return Math.max(totalResource - requestResource, 0);
    }

    @Override
    public Resource getResourceByUserId(String userId) {
        UserDO user = userdomapper.selectOne(new QueryWrapper<UserDO>().eq("user_id", userId));

        Map<String, Double> resourceMap = metricService.getUsedResourceByNamespace(userId);
        // 该用户已使用的cpu
        Double usedCpu = resourceMap.getOrDefault(ResourceConstants.CPU, (double) 0);
        // 该用户已使用的内存
        Double usedMemory = resourceMap.getOrDefault(ResourceConstants.MEMORY, (double) 0);

        VGpuAllocateVO vGpuAllocateVO = metricService.getUserAllocateVGpu(userId);
        // 该用户已分配的cpu
        Integer requestCpu = vGpuAllocateVO.getAllocatedCpuNum();
        // 该用户已分配的内存
        Integer requestMemory = vGpuAllocateVO.getAllocatedMemorySize();

        Double storageNum = metricService.getStorageByNamespace(userId);

        Resource resource = new Resource();
        resource.setCpu(user.getCpuNum());
        resource.setUsedCpu((float) DecimalUtils.get3DotNum(usedCpu));
        resource.setRemainedCpu((float) DecimalUtils.get3DotNum(user.getCpuNum() - DecimalUtils.get3DotNum(usedCpu)));
        resource.setCpuUsage(getUsagePercent(usedCpu, user.getCpuNum()));
        resource.setAllocatedCpu((float) requestCpu);
        resource.setRemainedAllocatedCpu((float) DecimalUtils.get3DotNum(user.getCpuNum() - requestCpu.doubleValue()));
        resource.setAllocatedCpuUsage(getUsagePercent(requestCpu, user.getCpuNum()));
        resource.setMemory(user.getMemorySize().floatValue());
        resource.setUsedMemory((float) DecimalUtils.get3DotNum(usedMemory));
        resource.setRemainedMemory((float) DecimalUtils.get3DotNum(user.getMemorySize() - DecimalUtils.get3DotNum(usedMemory)));
        resource.setMemoryUsage(getUsagePercent(usedMemory, user.getMemorySize()));
        resource.setAllocatedMemory((float) requestMemory);
        resource.setRemainedAllocatedMemory((float) DecimalUtils.get3DotNum(user.getMemorySize().floatValue() - DecimalUtils.get3DotNum(requestMemory.doubleValue())));
        resource.setAllocatedMemoryUsage(getUsagePercent(requestMemory, user.getMemorySize()));
        resource.setMemory(user.getMemorySize().floatValue());
        resource.setStorage(user.getVolumeSize());
        resource.setUsedStorage(storageNum.intValue());
        resource.setRemainedStorage(user.getVolumeSize() - storageNum.intValue());
        resource.setStorageUsage(getUsagePercent(storageNum, user.getVolumeSize()));
        resource.setAllocatedStorage(storageNum.intValue());
        resource.setRemainedAllocatedStorage(user.getVolumeSize() - storageNum.intValue());
        resource.setAllocatedStorageUsage(getUsagePercent(storageNum, user.getVolumeSize()));

        // 统计vGpu显存信息
        double usedVGpuMemory = getUserUsedVGpuMemory(userId);
        Integer userTotalVGpuMemory = user.getVgpuMemory();

        resource.setUsedVGpuMemory(usedVGpuMemory);
        resource.setTotalVGpuMemory(userTotalVGpuMemory);
        resource.setAllocatedTotalVGpuMemory(vGpuAllocateVO.getAllocatedVGpuMemory());
        resource.setAllocatedTotalVGpuMemoryUsage(getUsagePercent(vGpuAllocateVO.getAllocatedVGpuMemory(), userTotalVGpuMemory));
        resource.setUsedTotalVGpuMemoryUsage(getUsagePercent(usedVGpuMemory, userTotalVGpuMemory));
        return resource;
    }

    @Override
    public Double getUserUsedVGpuMemory(String userId) {
        UserDO userDO = userdomapper.selectOne(new QueryWrapper<UserDO>().eq("user_id", userId));
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("query", "sum(ORIONX_VGPU_MEMORY_USAGE" + "{client_id=\"" + userDO.getUsername() + "\"})");
        paramMap.put("time", System.currentTimeMillis() / 1000 + "");
        URI uri = urlEncode(externalConfig.getOrionRootGuiService(), externalConfig.getOrionRootGuiPort(), GET_ORION_VGPU_URL, paramMap);

        HttpGet httpGet = new HttpGet(uri);
        log.info("请求vGPU占用情况：{}", uri.toString());
        Integer usingVGpuMemory = 0;
        try {
            String responseResult = HttpUtil.request(httpGet);
            log.info("请求vGPU占用情况结果：{}", responseResult);
            usingVGpuMemory = parseVGpuMemoryUsageResponseJson(responseResult);
        } catch (Exception e) {
            log.error("请求vGPU占用情况失败: " + e);
        }

        return DecimalUtils.mb2Gb(usingVGpuMemory);
    }

    private Integer parseVGpuMemoryUsageResponseJson(String responseResult) {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            JsonNode jsonNode = objectMapper.readTree(responseResult);
            if (!jsonNode.findPath("value").isEmpty()) {
                Iterator<JsonNode> values = jsonNode.findPath("value").iterator();
                return jsonNode.findPath("value").get(1).asInt();
            }
        } catch (JsonProcessingException e) {
            log.error("json数据解析异常：", e);
        }
        return 0;
    }

    @Override
    public UserProfile getProfileByUserId(String userId) {
        UserProfile userProfile = new UserProfile();

        log.info("getProjectNumByUserId");
        userProfile.setProjectNum(getProjectNumByUserId(userId));
        log.info("getCodeStudioNumByUserId");
        userProfile.setCodeStudioNum(getCodeStudioNumByUserId(userId));
        log.info("getAlgorithmNumByUserId");
        userProfile.setAlgorithmNum(getAlgorithmNumByUserId(userId));
        log.info("getDatasourceNumByUserId");
        userProfile.setDataSourceNum(getDatasourceNumByUserId(userId));
        log.info("getImageNumByUserId");
        userProfile.setImageNum(getImageNumByUserId(userId));
        log.info("getTaskNumByUserId");
        userProfile.setTaskNum(getRunningTaskNumByUserId(userId));
        return userProfile;
    }

    /**
     * @Author zhangkun91535
     * @Description 运行中任务数
     * @Date 2022/11/14 17:15
     **/
    private Integer getRunningTaskNumByUserId(String userId) {
        return baseMapper.countRunningJobsByUserId(userId);
    }

    @Override
    public ResponseTokenDTO getLoginInfo(HttpServletRequest request) {
        String header = request.getHeader(KUBEFLOW_USERID);
        String userId = header.replace(HEAD_SUFFIX, "");

        UserDO userDO = userdomapper.selectOne(new QueryWrapper<UserDO>().eq("user_id", userId));
        String s3AccessKey = userDO.getS3AccessKey();
        String s3SecretKey = userDO.getS3SecretKey();
        String s3Token = getS3Token(s3AccessKey, s3SecretKey);
        ResponseTokenDTO responseTokenDTO = new ResponseTokenDTO();
        responseTokenDTO.setIsFirstLogin(userDO.getIsFirstLogin() == 1 ? Boolean.TRUE : Boolean.FALSE);
        responseTokenDTO.setUserId(userId);
        responseTokenDTO.setRole(userDO.getRole());
        responseTokenDTO.setS3Token(s3Token);

        // 将状态位设置为0，表示不是第一次登陆
        userDO.setIsFirstLogin(0);
        userdomapper.update(userDO, new UpdateWrapper<UserDO>().eq("user_id", userId));
        return responseTokenDTO;
    }

    /**
     * 算力利用率趋势图
     */
    @Override
    public ResourceUsageTrendVO getVGpuRatioUsageTrend(String userId, String timeCode) {
        return getUsageTrend(userId, timeCode, TrendChartChartTypeEnum.RATION.getType());
    }

    /**
     * 算力显存占用趋势图
     */
    @Override
    public ResourceUsageTrendVO getVGpuMemoryUsageTrend(String userId, String timeCode) {
        return getUsageTrend(userId, timeCode, TrendChartChartTypeEnum.MEMORY.getType());
    }

    @Override
    public VGpuInfoVO getVGpuInfo() {
        VGpuInfoVO vGpuInfoVO = new VGpuInfoVO();
        List<VGpuInfoVO.VGpu> vGpus = new ArrayList<>();
        List<ResourceGpu> gpuResources = GpuResourceConfig.getGpuResources();
        Map<String, Integer> vGpuNumSum = gpuResources.stream().collect(
                Collectors.groupingBy(
                        ResourceGpu::getGpuType,
                        Collectors.reducing(
                                0,
                                ResourceGpu::getVGpuNum,
                                Integer::sum
                        )
                )
        );
        for (String vGpuType : vGpuNumSum.keySet()) {
            VGpuInfoVO.VGpu vGpu = new VGpuInfoVO.VGpu();
            vGpu.setVGpuType(vGpuType);
            vGpu.setVGpuNum(vGpuNumSum.get(vGpuType));
            Integer vGpuSize = GpuResourceConfig.getGpuResourceByGpuType(vGpuType).getVGpuSize();
            vGpu.setVGpuMemory(vGpu.getVGpuNum() * vGpuSize);
            vGpus.add(vGpu);
        }

        vGpuInfoVO.setVGpus(vGpus);
        return vGpuInfoVO;
    }

    @Override
    public VGpuMemoryRemainVO getVGpuMemoryRemain(String userId) {
        UserDO user = userdomapper.selectOne(new QueryWrapper<UserDO>().eq("user_id", userId));
        VGpuMemoryRemainVO vGpuMemoryRemainVO = new VGpuMemoryRemainVO();

        List<Notebooks> notebooks = notebooksMapper.selectNotebooks(userId);
        Integer vGpuMemoryRemain = user.getVgpuMemory() - notebooks.stream().mapToInt(Notebooks::getVGpuMemory).sum();
        vGpuMemoryRemainVO.setVGpuMemoryRemain(vGpuMemoryRemain);
        List<VGpuMemoryRemainVO.GeniusVGpu> vGpuDisplayNames = new ArrayList<>();
        vGpuMemoryRemainVO.setVGpuDisplayNames(vGpuDisplayNames);
        for (GeniusVGpuEnum geniusVGpuEnum: GeniusVGpuEnum.values()) {
            VGpuMemoryRemainVO.GeniusVGpu geniusVGpu = new VGpuMemoryRemainVO.GeniusVGpu();
            geniusVGpu.setVGpuDisplayName(geniusVGpuEnum.getName());
            geniusVGpu.setVGpuDisplayValue(geniusVGpuEnum.getValue());
            geniusVGpu.setVGpuMemory(geniusVGpuEnum.getSize());
            vGpuDisplayNames.add(geniusVGpu);
        }
        return vGpuMemoryRemainVO;
    }

    /**
     * 资源占用趋势图
     * @return
     */
    @Override
    public ResourceUsageTrendVO getResourceUsingGraph(String timeCode, String trendChartType) {

        List<String> userNames = userdomapper.selectList(null).stream().map(UserDO::getUsername).collect(Collectors.toList());
        String nameSpaces = org.apache.commons.lang3.StringUtils.join(userNames, "|");

        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("start", TrendChartTimeStepEnum.getStartTimeStamp(timeCode) + "");
        paramMap.put("end", System.currentTimeMillis() / 1000 + "");
        paramMap.put("step", TrendChartTimeStepEnum.getTimeStep(timeCode));
        if (trendChartType.equals(ResourceUsingTrendChartChartTypeEnum.CPU_USING_TREND.getQueryType())) {
            paramMap.put("query", "sum (rate (container_cpu_usage_seconds_total"
                    + "{namespace=~\"" + nameSpaces + "\",image!=\"\",name=~\"^k8s_.*\",kubernetes_io_hostname=~\"^.*$\"}[1m])) by (io_kubernetes_pod_name)");
        }
        if (trendChartType.equals(ResourceUsingTrendChartChartTypeEnum.MEMORY_USING_TREND.getQueryType())) {
            paramMap.put("query", "(sum (container_memory_working_set_bytes"
                    + "{namespace=~\"" + nameSpaces + "\",image!=\"\",name=~\"^k8s_.*\",kubernetes_io_hostname=~\"^.*$\"}) by (io_kubernetes_pod_name))/1024/1024/1024");
        }
        if (trendChartType.equals(ResourceUsingTrendChartChartTypeEnum.PVC_USING_TREND.getQueryType())) {
            paramMap.put("query", "sum (kube_persistentvolumeclaim_resource_requests_storage_bytes{namespace=~\"" + nameSpaces + "\"})/1024/1024/1024");
        }
        URI uri = urlEncode(externalConfig.getGrafanaService(), Integer.parseInt(externalConfig.getGrafanaPort()), GET_RESOURCE_USING_TREND_GRAPH, paramMap);
        if (trendChartType.equals(ResourceUsingTrendChartChartTypeEnum.VGPU_RATIO_USING_TREND.getQueryType())) {
            paramMap.put("query", "sum (" + ResourceUsingTrendChartChartTypeEnum.getQueryWords(trendChartType) + ")");
            uri = urlEncode(externalConfig.getOrionRootGuiService(), externalConfig.getOrionRootGuiPort(), GET_ORION_VGPU_USAGE_TREND_URL, paramMap);
        }
        if (trendChartType.equals(ResourceUsingTrendChartChartTypeEnum.VGPU_MEMORY_USING_TREND.getQueryType())) {
            paramMap.put("query", "sum (" + ResourceUsingTrendChartChartTypeEnum.getQueryWords(trendChartType) + ") or vector(0)");
            uri = urlEncode(externalConfig.getOrionRootGuiService(), externalConfig.getOrionRootGuiPort(), GET_ORION_VGPU_USAGE_TREND_URL, paramMap);
        }

        HttpGet httpGet = new HttpGet(uri);
        log.info("请求资源占用趋势图url：{}", uri.toString());
        ResourceUsageTrendVO resourceUsageTrendVO = null;
        try {
            String responseResult = HttpUtil.request(httpGet);
            resourceUsageTrendVO = parseVGpuUsageResponseJson(responseResult);
        } catch (Exception e) {
            log.info("请求资源占用趋势图失败: " + e);
        }
        return resourceUsageTrendVO;
    }

    private ResourceUsageTrendVO getUsageTrend(String userId, String timeCode, String TrendChartType) {
        UserDO userDO = userdomapper.selectOne(new QueryWrapper<UserDO>().eq("user_id", userId));
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("query", "sum(" + TrendChartType + "{client_id=\"" + userDO.getUsername() + "\"}) or vector(0)");
        paramMap.put("start", TrendChartTimeStepEnum.getStartTimeStamp(timeCode) + "");
        paramMap.put("end", System.currentTimeMillis() / 1000 + "");
        paramMap.put("step", TrendChartTimeStepEnum.getTimeStep(timeCode));
        URI uri = urlEncode(externalConfig.getOrionRootGuiService(), externalConfig.getOrionRootGuiPort(), GET_ORION_VGPU_USAGE_TREND_URL, paramMap);

        HttpGet httpGet = new HttpGet(uri);
        log.info("请求vGPU利用趋势图url：{}", uri.toString());
        ResourceUsageTrendVO resourceUsageTrendVO = null;
        try {
            String responseResult = HttpUtil.request(httpGet);
            resourceUsageTrendVO = parseVGpuUsageResponseJson(responseResult);
        } catch (Exception e) {
            log.info("请求vGPU利用趋势图失败: " + e);
        }
        return resourceUsageTrendVO;
    }

    private String getS3Token(String accessKey, String secretKey) {
        Map<String, Object> stringObjectMap = new HashMap<>(MAP_SIZE);
        stringObjectMap.put("alg", "HS512");
        stringObjectMap.put("typ", "JWT");
        String payload = String.format(
                "{\"accessKey\":\"%s\", \"sub\": \"minio\", \"exp\":%d}",
                accessKey, System.currentTimeMillis() / SECONDS + S3_TOKEN_TIMEOUT
        );
        return Jwts.builder()
                .setHeader(stringObjectMap)
                .setPayload(payload)
                .signWith(SignatureAlgorithm.HS512, secretKey.getBytes())
                .compact();
    }

    private Integer getProjectNumByUserId(String userId) {
        try {
            return (Integer) Objects.requireNonNull(
                    restTemplate.getForObject(
                            UrlUtils.createUrl(
                                    externalConfig.getWebService(),
                                    externalConfig.getWebServicePort(), GET_PROJECT_NUM_URL
                            ), Response.class, userId
                    )
            ).getData();
        } catch (Exception e) {
            log.info("getProjectNumByUserId"+e);
            throw new AuthLoginException(AuthLoginExceptionEnum.FAIL_CONNECT_WEBSERVICE, e);
        }
    }

    private Integer getCodeStudioNumByUserId(String userId) {
        try {
            Response response = restTemplate.getForObject(
                    UrlUtils.createUrl(
                            externalConfig.getCodeStudioService(),
                            externalConfig.getCodeStudioServicePort(), GET_CODE_STUDIO_NUM_URL
                    ), Response.class, userId
            );
            assert response != null;
            NotebookResponse notebookResponse = JacksonUtils.objToObj(response.getData(), NotebookResponse.class);
            return notebookResponse.getTotal();
        } catch (Exception e) {
            log.info("getCodeStudioNumByUserId"+e);
            throw new AuthLoginException(AuthLoginExceptionEnum.FAIL_CONNECT_STUDIO_SERVICE, e);
        }
    }

    private Integer getDatasourceNumByUserId(String userId) {
        try {
            return (Integer) Objects.requireNonNull(
                    restTemplate.getForObject(
                            UrlUtils.createUrl(
                                    externalConfig.getWebService(),
                                    externalConfig.getWebServicePort(), GET_DATASOURCE_NUM_URL
                            ), Response.class, userId
                    )
            ).getData();
        } catch (Exception e) {
            log.info("getDatasourceNumByUserId"+e);
            throw new AuthLoginException(AuthLoginExceptionEnum.FAIL_CONNECT_WEBSERVICE, e);
        }
    }

    private Integer getAlgorithmNumByUserId(String userId) {
        try {
            return (Integer) Objects.requireNonNull(
                    restTemplate.getForObject(
                            UrlUtils.createUrl(
                                    externalConfig.getTrainingService(),
                                    externalConfig.getTrainingServicePort(), GET_ALGORITHM_NUM_URL
                            ), Response.class, userId
                    )
            ).getData();
        } catch (Exception e) {
            log.info("getAlgorithmNumByUserId"+e);
            throw new AuthLoginException(AuthLoginExceptionEnum.FAIL_CONNECT_TRAINING_SERVICE, e);
        }
    }

    private Integer getImageNumByUserId(String userId) {
        try {

            Response response = restTemplate.getForObject(
                    UrlUtils.createUrl(
                            externalConfig.getServiceManage(),
                            externalConfig.getServiceManagePort(), GET_AI_FLOW_IMAGE_NUM_URL
                    ), Response.class, userId
            );
            assert response != null;
            List notebookResponse = JacksonUtils.objToObj(response.getData(), List.class);
            return notebookResponse.size();
        } catch (Exception e) {
            log.info("getImageNumByUserId"+e);
            throw new AuthLoginException(AuthLoginExceptionEnum.FAIL_CONNECT_WEBSERVICE, e);
        }
    }

    private PageModel<UserVO> findUser() {
        List<UserDO> users = userdomapper.selectList(new QueryWrapper<UserDO>().orderByDesc("update_time"));
        int count = userdomapper.selectCount(new QueryWrapper<UserDO>(null));
        List<UserVO> user = new UserVoConverter(externalConfig, metricService, restTemplate).convertList(users, UserVO.class);
        return new PageModel<>(count, user);
    }

    private PageModel<UserVO> findUserWithKeyword(String keyword) {
        keyword = StringUtils.escapeChar(keyword.trim());
        List<UserDO> users = userdomapper.selectList(new QueryWrapper<UserDO>().like("username", keyword).orderByDesc("update_time"));
        int count = userdomapper.selectCount(new QueryWrapper<UserDO>().like("username", keyword));
        List<UserVO> user = new UserVoConverter(externalConfig, metricService, restTemplate).convertList(users, UserVO.class);
        return new PageModel<>(count, user);
    }

    private boolean checkOldPassword(String oldPassword, String account) {

        UserDO user = userdomapper.selectOne(new QueryWrapper<UserDO>().eq("username", account));
        if (user == null) {
            log.info(
                    "That the user:{} checked the original password is failed when changed the old password.",
                    account
            );
            return false;
        }

        boolean isPassed = BcryptUtil
                .bEncryptMatch(oldPassword, new String(ArrayUtils.toPrimitive(user.getPassword())));

        if (isPassed) {
            log.info(
                    "That the user:{} checked the original password is success when  changed the passowrd",
                    user.getUsername()
            );
        } else {
            log.info("用户： {}  该账号或者原密码错误。", user.getUsername());
        }
        return isPassed;
    }

    public static ResourceUsageTrendVO parseVGpuUsageResponseJson(String responseResult) {
        ResourceUsageTrendVO resourceUsageTrendVO = new ResourceUsageTrendVO();
        List<ResourceUsageTrendVO.Data> result = new ArrayList<>();
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            JsonNode jsonNode = objectMapper.readTree(responseResult);
            if (!jsonNode.findPath("values").isEmpty()) {
                Iterator<JsonNode> values = jsonNode.findPath("values").iterator();
                while (values.hasNext()) {
                    JsonNode dataNode = values.next();
                    ResourceUsageTrendVO.Data data = new ResourceUsageTrendVO.Data();
                    data.setX(String.valueOf(dataNode.get(0)));
                    data.setY(String.valueOf(dataNode.get(1)).replace("\"", ""));
                    result.add(data);
                }
            }
        } catch (JsonProcessingException e) {
            log.error("json数据解析异常：", e);
        }
        resourceUsageTrendVO.setResult(result);
        return resourceUsageTrendVO;
    }

    public static URI urlEncode(String host, Integer port, String path, Map<String, String> paramMap) {

        List params = new ArrayList();
        for (String keySet : paramMap.keySet()) {
            params.add(new BasicNameValuePair(keySet, paramMap.get(keySet)));
        }
        String param = URLEncodedUtils.format(params, "UTF-8");
        URI uri = null;
        try {
            uri = URIUtils.createURI(
                    "http", host, port,
                    path, param, null
            );
        } catch (URISyntaxException e) {
            log.error("url编码错误: " + e);
        }
        return uri;
    }
}
